<?php
namespace App\Common\Lib\Cache\KeyDB;

use App\Common as com;
use App\Common\Lib\Cache\KeyDB\DelayPool as nameDelayPoll;
use App\Common\Lib\Cache\KeyDB as nameKeyDB;
use \Hyperf\Redis\Redis as nameRedis;

/**
 * 持久层延迟写入池
 * @property com\Cla\ClaBase\DataStore $dataStore
 */
class DelayPool extends com\Cla\ClaBase {

	use com\Cla\ClaBase\Traits\GoPool;

	public string $delayPoolKey = 'delayPoolKey';

	/**
	 * 每次refreshOfOne数量限制
	 * @var int
	 */
	private int $refreshOfOneLimit = 100;

	/**
	 * @return nameRedis
	 */
	public function goRedis(  ):nameRedis {
		return $this->goPool( 'goRedis', function(){
			return com\Lib\Hyperf\Container::getRedis();
		} );
	}


	/**
	 * 同$keyName会被覆盖
	 * @param string $keyName
	 * @param int $delay 延迟
	 * @return bool
	 */
	public function addPool( string $keyName, int $delay ):bool {
		if( $delay < 0 ){
			throw com\Lib\Err\Exception::makeOfNew( '', com\Lib\Err\Code::$sysFunParamError );
		}
		//
		$time = com\Lib\Fun\Time::getTime() + $delay;
		//
		$existTime = $this->goRedis()->zRank( $this->delayPoolKey, $keyName );
		if( $existTime !== false && $existTime < $time ){
			// 已经存在且写入时间比先时间早的不需要处理
			return true;
		}
		//
		return $this->goRedis()->zAdd( $this->delayPoolKey, [], $time, $keyName ) > 0;
	}

	/**
	 * @param string $keyName
	 * @return bool
	 */
	public function isExistPool( string $keyName ):bool {
		return $this->goRedis()->zRank( $this->delayPoolKey, $keyName ) !== false;
	}

	/**
	 * @param string $keyName
	 * @return bool
	 */
	public function delPool( string $keyName ):bool {
		return $this->goRedis()->zRem( $this->delayPoolKey, $keyName ) > 0;
	}

	/**
	 * 回写延迟的数据(写入全部可以写入的数据)
	 * @return int 回写成功的数量
	 */
	public function refreshOfOne(  ):int {
		$successNumber = 0;
		$endTime = com\Lib\Fun\Time::getTime();
		while( true ){
			$datas = $this->goRedis()->zRangeByScode( $this->delayPoolKey, '-inf', (string)$endTime, [
				'limit' => [ 0, $this->refreshOfOneLimit ]
			] );
			if( empty( $datas ) ){
				break;
			}
			$keyDbReuse = nameKeyDB::use( '' );
			foreach( $datas as $dataValue ){
				// 处理一个删除一个，减少并发问题的发生概率
				$this->goRedis()->zRem( $this->delayPoolKey, $dataValue );
				// 立即回写
				try {
					$keyDbReuse->changeKey( $dataValue )->goPersistence()->writeBack();
					//
					$successNumber++;
				}catch( \Exception $e ){
				   // 发生异常时重新抛入队列(延迟1秒)
					$this->addPool( $dataValue, 1 );
					// 抛出简单的异常提示
					echo com\Lib\Err\Exception::getSimpleShowExceptionInfo( $e );
				}
			}
		}
		//
		return $successNumber;
	}

	/**
	 * 回写延迟的数据(循环)
	 * @return bool
	 */
	public function refreshOfLoop(  ):bool {
		while( true ){
			$this->refreshOfOne();
			// 等待一秒
			\Swoole\Coroutine::sleep( 1 );
		}
		//
		return true;
	}
	
	// ===============================
	//
	//
	// 下面为静态方法
	//
	//
	// ===============================


	
}
